#pragma once

#include <QVariant>

#include <utils/Concepts.h>
#include <utils/Describe.h>
#include <utils/Map.h>
#include <utils/config.h>
#include <utils/para_utils/QtUtils.h>


#include <boost/describe.hpp>
#include <boost/mp11.hpp>

namespace solar
{
    template <ClassHasName NamedClass>
    void setPara(const std::string& property_type, const QVariant& value, NamedClass* object)
    {
        ASSUME(!property_type.empty());
        ASSUME(object);
        auto* private_member_index = internal::describe::getPrivateMemberIndex<NamedClass>();
        auto private_member_index_const =
            internal::describe::getPrivateMemberIndexConst<NamedClass>();
        using D1 = boost::describe::describe_members<std::remove_reference_t<NamedClass>,
                                                     boost::describe::mod_any_access>;
        if (private_member_index->empty()) [[unlikely]]
        {
            private_member_index->reserve(boost::mp11::mp_size<D1>::value);
            for (auto& entry : private_member_index_const.data)
            {
                private_member_index->emplace(entry);
            }
        }
        std::uint64_t index = -1;
        private_member_index->visit(property_type, [&](auto& entry) { index = entry.second; });
        try
        {
            internal::qt_extension::setPara<boost::mp11::mp_size<D1>::value>(index, value, object);
        }
        catch (const std::invalid_argument&)
        {
            ::solar::println(::solar::format("ERROR: {}::setPara(): No such property: {}",
                                             NamedClass::name(), property_type));
            throw ::solar::format("ERROR: {}::setPara(): No such property: {}", NamedClass::name(),
                                  property_type);
        }
    }

    template <ClassHasName NamedClass>
    [[nodiscard]] auto getPara(const std::string& property_type, const NamedClass* const object)
        -> QVariant
    {
        ASSUME(!property_type.empty());
        ASSUME(object);
        auto* private_member_index = internal::describe::getPrivateMemberIndex<NamedClass>();
        auto private_member_index_const =
            internal::describe::getPrivateMemberIndexConst<NamedClass>();
        using D1 = boost::describe::describe_members<std::remove_reference_t<NamedClass>,
                                                     boost::describe::mod_any_access>;
        if (private_member_index->empty()) [[unlikely]]
        {
            private_member_index->reserve(boost::mp11::mp_size<D1>::value);
            for (auto& entry : private_member_index_const.data)
            {
                private_member_index->emplace(entry);
            }
        }
        std::uint64_t index = -1;
        private_member_index->visit(property_type, [&](auto& entry) { index = entry.second; });
        try
        {
            return internal::qt_extension::getPara<boost::mp11::mp_size<D1>::value>(index, object);
        }
        catch (const std::invalid_argument&)
        {
            ::solar::println(::solar::format("ERROR: {}::getPara(): No such property: {}",
                                             NamedClass::name(), property_type));
            throw ::solar::format("ERROR: {}::getPara(): No such property: {}", NamedClass::name(),
                                  property_type);
        }
    }
} // namespace solar
